import logging
logging.basicConfig(level=logging.DEBUG)
from PySide6.QtCore import Qt, QUrl, QRect, QPropertyAnimation, QEasingCurve, QByteArray
from PySide6.QtGui import QPixmap, QColor, QBrush,QStandardItemModel, QStandardItem,QAction
from pathlib import Path
import sys
from PySide6.QtWidgets import (QMainWindow, QApplication, QFileDialog, QPushButton,
    QMessageBox, QLabel, QListView, QTreeWidget, QTreeWidgetItem, QListWidget, QMenu)

from PySide6.QtWidgets import QAbstractItemView,QVBoxLayout,QScrollBar

from PySide6.QtMultimedia import QMediaPlayer, QAudioOutput
from ui_mainwindow import Ui_MainWindow
from mutagen._file import File
from mutagen.flac import FLAC
from mutagen.mp3 import MP3
import time
import os
import typing
import json
from mutagen.mp3 import MP3
from mutagen.flac import FLAC

class MainWindow(QMainWindow, Ui_MainWindow):
    def __init__(self):
        super().__init__()
        self.setupUi(self)
        # 初始化右键菜单
        self.context_menu = QMenu(self)
        self.delete_action = QAction('删除选中项', self)
        self.export_action = QAction('导出选中项', self)
        self.context_menu.addAction(self.delete_action)
        self.context_menu.addAction(self.export_action)
        self.delete_action.triggered.connect(self.delete_selected_items)
        self.export_action.triggered.connect(self.export_selected_items)
        
        # 初始化歌词数据
        self.lyrics_data = {}
        self.current_lyric_index = 0
        self.lyrics_list = self.findChild(QListWidget, "lyrics_list")
        if not self.lyrics_list:
            logging.error("未能找到歌词列表控件lyrics_list")
            self.lyrics_list = QListWidget()  # 创建备用控件
        self.scroll_anim = QPropertyAnimation()
        self.scroll_anim.setPropertyName(b"value")  # 绑定到滚动条的 value 属性
        self.scroll_anim.setTargetObject(self.lyrics_list.verticalScrollBar())  # 绑定到滚动条


        # 初始化媒体组件
        self.media_player = QMediaPlayer()
        self.audio_output = QAudioOutput()
        self.media_player.setAudioOutput(self.audio_output)
        self.media_player.errorOccurred.connect(self.handle_media_error) #播放错误调试信息
        self.audio_output.setVolume(0.5)  # 设置初始音量50%
        self.media_player.setAudioOutput(self.audio_output)
        self.volume_control.valueChanged.connect(lambda v: self.audio_output.setVolume(v/100))  # 连接音量控制信号
        # 连接播放状态信号
        self.media_player.mediaStatusChanged.connect(self.handle_media_status)
        # 连接播放进度信号
        self.media_player.positionChanged.connect(self.update_lyrics)
        self.media_player.positionChanged.connect(self.update_progress)
        self.media_player.durationChanged.connect(self.update_duration)  # 新增信号连接
        self.progress_slider.sliderMoved.connect(self.seek_position)
        # 创建垂直布局
        self.verticalLayout = QVBoxLayout(self.centralwidget)
        # 初始化表格模型
        # 初始化音乐信息列表模型
        self.music_info_model = QStandardItemModel()
        
        # 初始化播放列表
        self.playlist = self.findChild(QTreeWidget, "play_list")
        if self.playlist is not None:
            self.playlist.setHeaderLabels(['序号','歌曲名称', '演唱者', '专辑名称', '时长', '比特率', '格式'])
            # 设置多选模式
            self.playlist.setSelectionMode(QAbstractItemView.ExtendedSelection)
            # 初始化当前选中项
            if self.playlist.topLevelItemCount() > 0:
                self.playlist.setCurrentItem(self.playlist.topLevelItem(0))
        else:
            logging.error("未能找到播放列表控件play_list")
            self.playlist = QTreeWidget()  # 创建备用播放列表控件
            self.playlist.setHeaderLabels(['序号','歌曲名称', '演唱者', '专辑名称', '时长', '比特率', '格式'])
            self.verticalLayout.addWidget(self.playlist)
        # 安全设置列对齐方式
        if self.playlist:
            # 确保存在表头项
            if not self.playlist.headerItem():
                self.playlist.setHeaderLabels(['序号','歌曲名称', '演唱者', '专辑名称', '时长', '比特率', '格式'])
            
            for col in range(7):
                header = self.playlist.headerItem()
                if col in [0, 6]:
                    header.setTextAlignment(col, Qt.AlignmentFlag.AlignCenter | Qt.TextFlag.TextWordWrap)
                elif col in [4, 5]:
                    header.setTextAlignment(col, Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignVCenter)
                # 统一设置列宽
        columns = [
            (0, 50),    # 序号
            (1, 120),   # 歌曲名称
            (2, 80),    # 演唱者
            (3, 120),   # 专辑名称
            (4, 50),    # 时长
            (5, 80),    # 比特率
            (6, 60)     # 格式
        ]
        if self.playlist:
            for col, width in columns:
                self.playlist.setColumnWidth(col, width)
        else:
            logging.error("播放列表控件未找到，无法设置列宽")
        #        self.playlist.resizeColumnToContents(1)  # 自动宽度的语法
        # 连接双击信号
        if self.playlist:
            self.playlist.doubleClicked.connect(self.handle_double_click)
        # 启用自定义上下文菜单
        self.playlist.setContextMenuPolicy(Qt.CustomContextMenu)
        self.playlist.customContextMenuRequested.connect(self.show_context_menu)
        # 初始化当前选中项
        self.playlist.setCurrentItem(self.playlist.topLevelItem(0))

        # 初始化封面缓存
        self.default_cover = QPixmap("icons/default_cover.png")
        
        # 初始化播放控制状态
        self.is_playing = False
        self.current_file = None
        self.cycle_mode = 0  # 初始化播放模式
        
        # 合并音量控制信号
        self.volume_control.setValue(50)
        self.volume_control.valueChanged.emit(50)  # 手动触发信号同步
        self.volume_control.valueChanged.connect(lambda v: (
            self.audio_output.setVolume(v / 100),
            self.update_volume_display(v)
        ))
        self.volume_number.display(50)  # 初始化显示数值
        
        # 连接按钮信号
        self.actionImport_audio.triggered.connect(self.import_audio)
        self.actionImport_audio_folder.triggered.connect(self.import_audio_folder)
        self.actionImport_playlist.triggered.connect(self.import_playlist)
        # 初始化所有控制按钮
        # 初始化所有控制按钮
        self.play_button = self.findChild(QPushButton, "play_button")
        self.stop_button = self.findChild(QPushButton, "stop_button")
        self.next_button = self.findChild(QPushButton, "nex_button")
        self.prev_button = self.findChild(QPushButton, "pre_button")
        self.cycle_button = self.findChild(QPushButton, "cycle_button")
        
        # 连接循环模式按钮点击事件
        if self.cycle_button:
            self.cycle_button.clicked.connect(self.toggle_cycle)
        # 安全连接播放控制按钮
        if self.play_button:
            self.play_button.clicked.connect(self.play)
        if self.stop_button:
            self.stop_button.clicked.connect(self.stop)
        if self.next_button:
            self.next_button.clicked.connect(self.next_song)
        if self.prev_button:
            self.prev_button.clicked.connect(self.previous_song)

        # 在初始化完成后加载播放列表
        self.load_playlist()

    def import_audio_folder(self):
        folder_path = QFileDialog.getExistingDirectory(self, '选择音频文件夹')
        if folder_path:
            import os
            for root, dirs, files in os.walk(folder_path):
                for file in files:
                    if file.lower().endswith(('.mp3', '.flac')):
                        self._add_to_playlist(os.path.join(root, file))

    def import_audio(self):
        file_paths, _ = QFileDialog.getOpenFileNames(self, '选择音频文件', '', '音频文件 (*.mp3 *.flac)')
        for path in file_paths:
            self._add_to_playlist(path)

    def actionImport_playlist(self):
        self.actionImport_playlist = QAction('导入播放列表', self)
        self.menuFile.insertAction(self.actionImport_audio_folder, self.actionImport_playlist)
        self.actionImport_playlist.triggered.connect(self.import_playlist)

    def import_playlist(self):
        path, _ = QFileDialog.getOpenFileName(self, '导入播放列表', '', 'M3U Files (*.m3u)')
        if path:
            try:
                with open(path, 'r', encoding='utf-8') as f:
                    for line in f:
                        line = line.strip()
                        if line and not line.startswith('#'):
                            file_path = os.path.normpath(line)
                            if os.path.isfile(file_path):
                                self._add_to_playlist(file_path)
                            else:
                                QMessageBox.warning(self, '无效文件', f'文件不存在: {file_path}')
                self.save_playlist()
            except Exception as e:
                QMessageBox.critical(self, '导入错误', f'播放列表导入失败:\n{str(e)}')

    def _add_to_playlist(self, file_path):
        # 重复文件检查
        normalized_path = os.path.normcase(os.path.abspath(file_path))
        for i in range(self.playlist.topLevelItemCount()):
            item = self.playlist.topLevelItem(i)
            existing_path = os.path.normcase(os.path.abspath(item.data(6, Qt.ItemDataRole.UserRole)))
            if existing_path == normalized_path:
                QMessageBox.information(self, "重复文件", f"文件已存在于播放列表:\n{os.path.basename(file_path)}")
                return
        try:
            # 先尝试获取内嵌歌词
            embedded_lyrics = self._get_embedded_lyrics(file_path)
            
            # 仅存储歌词数据，不更新界面
            self.lyrics_data[file_path] = self.parse_lrc(embedded_lyrics) if embedded_lyrics else []

            audio = File(file_path)
            
            # 分离元数据获取方法
            def _get_mp3_tag(tags, *keys):
                for key in keys:
                    try:
                        return tags[key].text[0] if tags[key].text else '未知'
                    except (KeyError, AttributeError, IndexError):
                        continue
                return '未知'

            def _get_flac_tag(tags, *keys):
                for key in keys:
                    try:
                        return tags[key][0] if tags[key] else '未知'
                    except KeyError:
                        continue
                return '未知'

            # 获取格式特定元数据
            if isinstance(audio, MP3):
                audio = typing.cast(MP3, audio)
                if audio is not None and audio.info is not None:
                    info = audio.info
                    tags = audio.tags if (audio is not None and audio.tags) else {}
                    title = _get_mp3_tag(tags, 'TIT2', 'TIT1')
                    artist = _get_mp3_tag(tags, 'TPE1', 'TPE2')
                    album = _get_mp3_tag(tags, 'TALB', 'TAL')
                    bitrate = f'{round(info.bitrate/1000)} kbps'
                    file_format = 'MP3'
            elif isinstance(audio, FLAC):
                audio = typing.cast(FLAC, audio)
                if audio is not None:
                    info = audio.info
                    tags = audio.tags if (audio is not None and audio.tags) else {}
                    title = _get_flac_tag(tags, 'TITLE', 'title')
                    artist = _get_flac_tag(tags, 'ARTIST', 'artist')
                    album = _get_flac_tag(tags, 'ALBUM', 'album')
                    file_size = Path(file_path).stat().st_size
                    duration = audio.info.length
                    bitrate = f'{int((file_size * 8) / max(duration, 1))} Kbps' if duration > 0 else '未知'
                    file_format = 'FLAC'
                
                # 获取FLAC封面
                cover_data = next((pic.data for pic in audio.pictures if pic.type == 3), None) if audio.pictures else None

            # 处理时长
            duration = round(audio.info.length)
            minutes = duration // 60
            seconds = duration % 60
            duration_str = f"{minutes}:{seconds:02d}"
            
            # 处理比特率
            if isinstance(audio, FLAC):
                file_size = Path(file_path).stat().st_size
                bitrate = int((file_size * 8) / (duration if duration > 0 else 1) / 1000)
                bitrate_str = f"{bitrate} kbps"
            else:
                bitrate = getattr(audio.info, 'bitrate', 0)
                bitrate_str = f"{round(bitrate)} kbps" if bitrate > 0 else "未知"
            if isinstance(audio, MP3):
                bitrate_rounded = round(bitrate / 1000)
                bitrate_str = f"{bitrate_rounded} kbps" if bitrate > 0 else "未知"
            else:
                bitrate_str = f"{int(bitrate)} kbps" if bitrate > 0 else "未知"
            
            # 获取文件格式
            file_format = 'MP3' if file_path.lower().endswith('.mp3') else 'FLAC'
            
            # 调试日志
            # logging.info(f'解析到FLAC元数据: title={title} artist={artist} album={album} duration={duration_str} bitrate={bitrate_str}')
            
            # 创建表格行数据
            item = QTreeWidgetItem([
                str(self.playlist.topLevelItemCount() + 1),
                title,
                artist,
                album,
                duration_str,
                bitrate_str,
                file_format
            ])
            # 设置序号列和格式列居中
            item.setTextAlignment(0, Qt.AlignmentFlag.AlignCenter | Qt.TextFlag.TextWordWrap) # 序号列
            item.setTextAlignment(4, Qt.AlignmentFlag.AlignRight) # 时长列
            item.setTextAlignment(5, Qt.AlignmentFlag.AlignRight) # 比特率列
            item.setTextAlignment(6, Qt.AlignmentFlag.AlignCenter | Qt.TextFlag.TextWordWrap) # 格式列  
            

            # 将文件路径存储在最后一列（格式列）的UserRole
            # 在指定列设置数据（格式列第6列）
            item.setData(6, Qt.ItemDataRole.UserRole, file_path)
            item.setData(6, Qt.ItemDataRole.UserRole + 1, {
                'title': title,
                'artist': artist,
                'album': album,
                'bitrate':bitrate_str,
                'audioformat':file_format,
                'duration': duration_str
            })
            self.playlist.addTopLevelItem(item)
            
            # 提取专辑图片
            cover_data = None
            try:
                if isinstance(audio, MP3):
                    # 尝试多种封面标签
                    for tag in ['APIC:cover', 'APIC', 'Cover Art (Front)']:
                        if tag in audio.tags:
                            cover_data = audio.tags[tag].data
                            break
                elif isinstance(audio, FLAC):
                    if hasattr(audio, 'pictures'):
                        # 寻找封面类型图片(类型3)
                        for pic in audio.pictures:
                            if pic.type == 3:  # 3表示封面图片
                                cover_data = pic.data
                                break
            except Exception as e:
                logging.error(f"MP3封面解析异常: {str(e)}，可用APIC帧：{audio.tags.getall('APIC')}")
            
            # 缓存最后成功的封面
            if cover_data:
                self.last_valid_cover = cover_data
            else:
                cover_data = getattr(self, 'last_valid_cover', None)
            
            # 直接在此处处理封面更新
            self.update_cover_image(cover_data)

            # 更新音乐信息显示
            # 删除冗余的UI更新代码

            # 将元数据直接存储在树形控件项中
            item.setData(6, Qt.ItemDataRole.UserRole + 1, {
                'title': title,
                'artist': artist,
                'album': album,
                'bitrate':bitrate_str,
                'audioformat':file_format,
                'duration': duration_str
            })
            
            # 自动同步模型更新
            self.music_info_model.layoutChanged.emit()

        except Exception as e:
            logging.error(f"解析文件失败: {str(e)} - 文件路径: {file_path}")
            # 在UI显示错误提示
            QMessageBox.warning(self, "文件加载失败", 
                f"无法加载文件:\n{Path(file_path).name}\n\n错误详情: {str(e)}",
                QMessageBox.StandardButton.Ok)

    def update_volume_display(self, value):
        self.volume_number.display(value)

    def handle_media_error(self, error, error_string):
        QMessageBox.warning(self, "播放错误", f"媒体播放错误({error}): {error_string}")

    def play(self):
        if self.media_player.playbackState() == QMediaPlayer.PlaybackState.PlayingState:
            self.media_player.pause()
            self.play_button.setText("播放")
        else:
            self.media_player.play()
            self.play_button.setText("暂停")

    def stop(self):
        self.media_player.stop()
        self.play_button.setText("播放")
        self.progress_slider.setValue(0)
        self.time_label.setText("00:00 / 00:00")


    def next_song(self):
        if self.cycle_mode == 1:
            self.play_random_song()
        else:
            current_row = self.playlist.indexOfTopLevelItem(self.playlist.currentItem())
            if current_row < self.playlist.topLevelItemCount() - 1:
                self.playlist.setCurrentItem(self.playlist.topLevelItem(current_row + 1))
                self.handle_double_click(self.playlist.currentIndex())
            elif self.cycle_mode == 0:
                self.stop()

    def play_random_song(self):
        import random
        count = self.playlist.topLevelItemCount()
        if count > 0:
            random_row = random.randint(0, count-1)
            self.playlist.setCurrentItem(self.playlist.topLevelItem(random_row))
            self.handle_double_click(self.playlist.currentIndex())

    def previous_song(self):
        if self.cycle_mode == 1:
            self.play_random_song()
        else:
            current_row = self.playlist.indexOfTopLevelItem(self.playlist.currentItem())
            if current_row > 0:
                self.playlist.setCurrentItem(self.playlist.topLevelItem(current_row - 1))
                self.handle_double_click(self.playlist.currentIndex())

    def toggle_cycle(self):
        # 切换播放模式
        self.cycle_mode = (self.cycle_mode + 1) % 3
        mode_names = ["顺序播放", "随机播放", "单曲循环"]
        self.cycle_button.setText(mode_names[self.cycle_mode])
        self.cycle_button.setToolTip(f"当前模式：{mode_names[self.cycle_mode]}")

    def load_cover_image(self, file_path):
        try:
            audio = File(file_path)
            cover_data = None
            
            if isinstance(audio, MP3):
                if audio.tags:
                    for apic in audio.tags.getall('APIC'):
                        if apic.type == 3:
                            cover_data = apic.data
                            break
                    else:
                        apics = audio.tags.getall('APIC')
                        if apics:
                            cover_data = apics[0].data
                    logging.info(f'找到{len(audio.tags.getall("APIC"))}个APIC帧')
            elif isinstance(audio, FLAC):
                cover_data = next((pic.data for pic in audio.pictures if pic.type == 3), None)
            # 网络获取备用（需要lastfm模块）
            if not cover_data:
                # 通过文件路径查找对应的播放列表项
                for i in range(self.playlist.topLevelItemCount()):
                    item = self.playlist.topLevelItem(i)
                    if item.data(6, Qt.ItemDataRole.UserRole) == file_path:
                        meta = item.data(6, Qt.UserRole + 1)
                        if meta.get('artist') != '未知' and meta.get('album') != '未知':
                            try:
                                from lastfm import get_album_cover
                                cover_data = get_album_cover(meta['artist'], meta['album'])
                                break
                            except Exception as e:
                                logging.warning(f"网络封面获取失败: {str(e)}")
                        break
            
            self.update_cover_image(cover_data)
            
        except Exception as e:
            logging.warning(f"封面加载失败: {str(e)}")
            self.update_cover_image(None)

    def update_cover_image(self, cover_data):
        try:
            if cover_data:
                pixmap = QPixmap()
                if not pixmap.loadFromData(cover_data):
                    raise ValueError("无效的图片数据")
            else:
                pixmap = self.default_cover
            self.cover_label.setPixmap(pixmap.scaled(200, 200, Qt.KeepAspectRatio, Qt.SmoothTransformation))
        except Exception as e:
            logging.error(f"封面加载失败: {str(e)}")
            self.cover_label.setPixmap(self.default_cover.scaled(200, 200, Qt.KeepAspectRatio, Qt.SmoothTransformation))


    

    
    def update_music_info(self, title, artist, album, audioformat, bitrate):
        self.music_info_model.clear()
        song_item = QStandardItem(f"歌曲: {title}")
        artist_item = QStandardItem(f"歌手: {artist}")
        album_item = QStandardItem(f"专辑: {album}")
        bitrate_item = QStandardItem(f"{bitrate}")
        audioformat_item = QStandardItem(f"{audioformat}")
        self.music_info_model.appendRow(song_item)
        self.music_info_model.appendRow(song_item)
        
        bold_font = song_item.font()
        bold_font.setBold(True)
        song_item.setFont(bold_font)
        
        self.music_info_model.appendRow(song_item)
        self.music_info_model.appendRow(artist_item)
        self.music_info_model.appendRow(album_item)
        
        # 设置字体大小和自动换行
        for item in [song_item, artist_item, album_item]:
            item.setData(12, Qt.ItemDataRole.FontRole)
            item.setData(Qt.TextFlag.TextWordWrap, Qt.ItemDataRole.TextAlignmentRole)

        self.audio_format.setText(audioformat)
        self.bit_rate.setText(bitrate)
        self.artist_name.setText(artist)
        self.album_name.setText(album)

    def handle_double_click(self, index):
        # 获取当前选中歌曲信息
        current_item = self.playlist.itemFromIndex(index)
        if not current_item:
            return
        if current_item:
            # 获取歌曲元数据
            title = current_item.text(1)
            artist = current_item.text(2)
            album = current_item.text(3)
            audioformat = current_item.text(6)
            bitrate = current_item.text(5)


            
            # 更新界面信息
            self.artist_name.setText(artist)
            self.album_name.setText(album)
            self.audio_format.setText(audioformat)
            self.bit_rate.setText(bitrate)
            self.update_music_info(title, artist, album, audioformat, bitrate)
            # 从UserRole获取完整文件路径
            file_path = current_item.data(6, Qt.ItemDataRole.UserRole)

            # 加载封面图片
            self.load_cover_image(file_path)

            # 切换歌曲时加载歌词
            self.lyrics_list.clear()
            self.update_music_info(title, artist, album, audioformat, bitrate)
            # 从UserRole获取完整文件路径
            file_path = current_item.data(6, Qt.ItemDataRole.UserRole)
            
            # 加载封面图片
            self.load_cover_image(file_path)
            
            # 切换歌曲时加载歌词
            self.lyrics_list.clear()
            if file_path in self.lyrics_data:
                for _, text in self.lyrics_data[file_path]:
                    self.lyrics_list.addItem(text)

            # 更新音乐信息模型
            self.music_info_model.clear()
            # 从UserRole获取完整歌曲信息
            # 直接从树形控件项获取元数据
            data = current_item.data(6, Qt.ItemDataRole.UserRole + 1)
            if not data:
                data = {'title': '未知', 'artist': '未知', 'album': '未知'}
            title = data.get('title', '未知')
            artist = data.get('artist', '未知')
            album = data.get('album', '未知')
            
            # 创建新的模型项
            song_item = QStandardItem(f"歌曲: {title}")
            artist_item = QStandardItem(f"歌手: {artist}")
            album_item = QStandardItem(f"专辑: {album}")
            
            # 添加到模型
            self.music_info_model.appendRow(song_item)
            self.music_info_model.appendRow(artist_item)
            self.music_info_model.appendRow(album_item)
            
            # 触发视图更新
        selected_item = self.playlist.selectedItems()[0]
        if selected_item:
            try:
                # 获取存储在第5列（UserRole）的文件路径
                file_path = selected_item.data(6, Qt.ItemDataRole.UserRole)
                if not Path(file_path).exists():
                    raise FileNotFoundError(f"文件不存在: {file_path}")
                
                # 停止当前播放并重置播放器
                self.media_player.stop()
                self.media_player.setSource(QUrl.fromLocalFile(file_path))
                self.current_file = file_path  # 更新当前文件路径
                
                
                # 更新界面显示
                self.music_info.setTitle(f"playing: {selected_item.text(1)}")
                self.play_button.setText("暂停")
                
                # 开始播放并连接状态更新
                self.media_player.play()
                self.media_player.playbackStateChanged.connect(self.update_play_button)
                # 连接播放进度信号
                
                # 连接时长更新信号
                self.media_player.durationChanged.connect(self.update_duration)
                
            except Exception as e:
                QMessageBox.critical(self, "播放错误",
                    f"无法播放文件:\n{str(e)}\n\n文件路径: {file_path}",
                    QMessageBox.StandardButton.Ok)

    def update_lyrics(self, position):
        if not self.current_file or self.current_file not in self.lyrics_data:
            self.lyrics_list.clear()
            return
        current_lyrics = self.lyrics_data[self.current_file]
        if not current_lyrics:
            self.lyrics_list.clear()
            return

        # 转换时间为秒并查找当前歌词索引
        current_time = position
        low, high = 0, len(current_lyrics) - 1
        while low <= high:
            mid = (low + high) // 2
            if current_time < current_lyrics[mid][0]:
                high = mid - 1
            else:
                low = mid + 1

        # 确定最接近的索引
        self.current_lyric_index = high if high >= 0 else low
        

        if 0 <= self.current_lyric_index < len(current_lyrics):
            # 清除所有项样式

            for i in range(self.lyrics_list.count()):
                item = self.lyrics_list.item(i)
                item.setBackground(QBrush(Qt.NoBrush))  # 设置为透明背景
                item.setForeground(QBrush(Qt.NoBrush))  # 恢复默认字体颜色
                item.setTextAlignment(Qt.AlignmentFlag.AlignCenter | Qt.TextFlag.TextWordWrap) #歌词水平居中
                
                font = item.font()
                font.setBold(False)
                item.setFont(font)

            # 设置当前歌词高亮
            current_item = self.lyrics_list.item(self.current_lyric_index)
            if current_item:
                current_item.setBackground(QColor(68, 74, 152))
                current_item.setForeground(Qt.white)
                font = current_item.font()
                font.setBold(True)
                current_item.setFont(font)

            # 滚动到居中位置
            self.lyrics_list.scrollTo(self.lyrics_list.indexFromItem(current_item), QAbstractItemView.ScrollHint.PositionAtCenter)
            #self.lyrics_list.scrollToItem(current_item, QAbstractItemView.ScrollHint.PositionAtCenter)
            # 精确计算居中位置
            current_item = self.lyrics_list.item(self.current_lyric_index)
            item_rect = self.lyrics_list.visualItemRect(current_item)
            viewport_height = self.lyrics_list.viewport().height()
            
            # 计算项中心点与视口中心点的偏移
            item_center = item_rect.top() + item_rect.height() // 2
            target_pos = item_center - viewport_height // 2
            
            # 边界检测防止越界
            if not self.lyrics_list or not self.lyrics_list.verticalScrollBar():
                return
            max_scroll = self.lyrics_list.verticalScrollBar()
            target_pos = max(0, min(target_pos, max_scroll.maximum()))

            # 创建并配置滚动动画
            if self.lyrics_list and self.lyrics_list.verticalScrollBar():
                # 确保动画对象存在
                self.scroll_anim = QPropertyAnimation()
                self.scroll_anim.setPropertyName(b"value")
                self.scroll_anim.setTargetObject(self.lyrics_list.verticalScrollBar())
                
                # 停止正在进行的动画
                if self.scroll_anim.state() == QPropertyAnimation.Running:
                    self.scroll_anim.stop()
                
                # 优化动画参数
                self.scroll_anim.setDuration(1600)  # 缩短动画时间
                self.scroll_anim.setEasingCurve(QEasingCurve.Type.OutQuad)  # 更平滑的缓动曲线
                self.scroll_anim.setStartValue(self.lyrics_list.verticalScrollBar().value())
                self.scroll_anim.setEndValue(target_pos)
                
                # 添加边界条件处理
                max_value = self.lyrics_list.verticalScrollBar().maximum()
                target_pos = min(max(target_pos, 0), max_value)
                
                # 确保动画完成后释放资源
                self.scroll_anim.finished.connect(lambda: self.scroll_anim.deleteLater())
                self.scroll_anim.stop()
            
            
            # 获取实际项高度
            item_height = self.lyrics_list.visualItemRect(current_item).height()
            viewport_height = self.lyrics_list.viewport().rect().height()
            


    def update_progress(self, position):
        # 更新进度条和时间显示
        duration = self.media_player.duration()
        if duration > 0:
            self.progress_slider.setValue(position)
            # 转换时间为分:秒格式
            minutes = position // 60000
            seconds = (position % 60000) // 1000
            duration_min = duration // 60000
            duration_sec = (duration % 60000) // 1000
            self.time_label.setText(f"{minutes}:{seconds:02d} / {duration_min}:{duration_sec:02d}")

    def seek_position(self, position):
        # 处理用户拖动进度条操作
        if self.media_player.playbackState() == QMediaPlayer.PlaybackState.PlayingState:
            self.media_player.pause()
        self.media_player.setPosition(position)
        if self.media_player.playbackState() != QMediaPlayer.PlaybackState.PlayingState:
            self.media_player.play()

    def handle_media_status(self, status):
        if status == QMediaPlayer.EndOfMedia:
            if self.cycle_mode == 0:  # 顺序播放
                self.next_song()
            elif self.cycle_mode == 1:  # 随机播放
                self.play_random_song()
            elif self.cycle_mode == 2:  # 单曲循环
                self.media_player.play()

    def update_play_button(self, state):
        self.play_button.setText("暂停" if state == QMediaPlayer.PlaybackState.PlayingState else "播放")


    def parse_lrc(self, lrc_content):
        print('正在解析歌词内容...')
        lyrics = []
        import re
        for line_num, line in enumerate(lrc_content.split('\n'), 1):
            if line.startswith('['):
                try:
                    # 修改正则表达式匹配分钟:秒.毫秒格式
                    time_match = re.match(r'\[(\d{2}):(\d{2}\.\d{3})\]', line)
                    if not time_match:
                        logging.warning(f'第{line_num}行时间格式错误: {line.strip()}')
                        continue
                    
                    minutes = int(time_match.group(1))
                    seconds_part = time_match.group(2).split('.')
                    seconds = int(seconds_part[0])
                    milliseconds = int(seconds_part[1].ljust(3, '0'))  # 补足三位毫秒
                    timestamp = (minutes * 60 + seconds) * 1000 + milliseconds
                    text = line.split(']', 1)[1].strip()
                    lyrics.append((timestamp, text))
                except Exception as e:
                    logging.warning(f'第{line_num}行解析失败: {str(e)} 内容: {line.strip()}')
        return sorted(lyrics, key=lambda x: x[0])

    def update_duration(self, duration):
        # 更新总时长显示
        self.progress_slider.setMaximum(duration)
        minutes = duration // 60000
        seconds = (duration % 60000) // 1000
        duration_str = f"{minutes}:{seconds:02d}"
        self.time_label.setText(f"00:00 / {duration_str}")

    def _get_embedded_lyrics(self, file_path):
        try:
            if file_path.lower().endswith('.mp3'):
                # 优先读取外置歌词文件
                lrc_path = Path(file_path).with_suffix('.lrc')
                if lrc_path.exists():
                    with open(lrc_path, 'rb') as f:
                        content = f.read()
                        # 自动检测编码
                        for encoding in ['utf-16','utf-16LE','gbk', 'big5']:
                            try:
                                return content.decode(encoding)
                            except UnicodeDecodeError:
                                continue
                # 没有外置歌词再读取内嵌歌词
                return self._get_embedded_mp3_lyrics(file_path)
            elif file_path.lower().endswith('.flac'):
                return self._get_embedded_flac_lyrics(file_path)
        except Exception as e:
            logging.warning(f'歌词解析失败: {str(e)}')
        return ''

    def _get_embedded_mp3_lyrics(self, file_path):
        from pathlib import Path
        from mutagen.id3 import ID3, USLT
        
        # 先检查外置歌词文件
        lrc_path = Path(file_path).with_suffix('.lrc')
        if lrc_path.exists():
            try:
                with open(lrc_path, 'rb') as f:
                    content = f.read()
                    # 复用已有的编码检测流程
                    for encoding in ['utf-8','utf-16','utf-16LE','gbk']:
                        try:
                            return content.decode(encoding)
                        except UnicodeDecodeError:
                            continue
            except Exception as e:
                print(f"读取外置歌词失败: {str(e)}")
        
        # 没有外置歌词时解析内嵌歌词
        try:
            audio = ID3(file_path)
            for tag in audio.values():
                if isinstance(tag, USLT):  # USLT是内嵌歌词的标签
                    return tag.text
            return None  # 如果没有找到歌词信息，返回None
        except Exception as e:
            print(f"Error reading lyrics from {file_path}: {e}")
            return None

    def _get_embedded_flac_lyrics(self, file_path):
        # 优先读取外置歌词文件
        lrc_path = Path(file_path).with_suffix('.lrc')
        if lrc_path.exists():
            try:
                with open(lrc_path, 'rb') as f:
                    content = f.read()
                    # 复用现有的多编码解析流程
                    for encoding in ['utf-8','utf-16','utf-16LE','gbk']:
                        try:
                            return content.decode(encoding)
                        except UnicodeDecodeError:
                            continue
            except Exception as e:
                logging.warning(f'外置歌词读取失败: {str(e)}')
        
        # 没有外置歌词时解析内嵌歌词
        try:
            audio = FLAC(file_path)
            if 'lyrics' in audio:
                return '\n'.join(audio['lyrics'])
            return ''
        except Exception as e:
            logging.error(f'FLAC歌词解析失败: {str(e)}')
            return ''

    def closeEvent(self, event):
        self.save_playlist()
        event.accept()

    def save_playlist(self):
        try:
            playlist_data = []
            for i in range(self.playlist.topLevelItemCount()):
                item = self.playlist.topLevelItem(i)
                file_path = item.data(6, Qt.ItemDataRole.UserRole)
                meta = item.data(6, Qt.ItemDataRole.UserRole + 1)
                playlist_data.append({
                    'file_path': str(Path(file_path).resolve()),
                    'title': meta['title'],
                    'artist': meta['artist'],
                    'album': meta['album'],
                    'duration': meta['duration'],
                    'bitrate': meta['bitrate'],
                    'format': meta['audioformat']
                })

            with open('playlist.json', 'w', encoding='utf-8') as f:
                json.dump(playlist_data, f, indent=2, ensure_ascii=False)
        except Exception as e:
            logging.error(f'保存播放列表失败: {str(e)}')

    def show_context_menu(self, pos):
        # 转换坐标到全局坐标系
        global_pos = self.playlist.viewport().mapToGlobal(pos)
        # 显示右键菜单
        self.context_menu.exec(global_pos)

    def delete_selected_items(self):
        selected_items = self.playlist.selectedItems()
        if not selected_items:
            return

        reply = QMessageBox.question(self, '确认删除', '确定要删除选中的{}项吗？'.format(len(selected_items)))
        if reply == QMessageBox.StandardButton.Yes:
            # 反向删除避免索引变化
            for item in reversed(selected_items):
                (self.playlist.invisibleRootItem() if item.parent() is None else item.parent()).removeChild(item)
            
            # 立即保存并刷新界面
            self.save_playlist()
            self.playlist.viewport().update()
            
            # 重排所有项目序号
            for index in range(self.playlist.topLevelItemCount()):
                item = self.playlist.topLevelItem(index)
                item.setText(0, str(index + 1))

    def export_selected_items(self):
        selected_items = self.playlist.selectedItems()
        if not selected_items:
            return

        path, _ = QFileDialog.getSaveFileName(self, '导出播放列表', '', 'M3U Playlist Files (*.m3u)')
        if path:
            with open(path, 'w', encoding='utf-8') as f:
                f.write('#EXTM3U\n')
                for item in selected_items:
                    file_path = os.path.normpath(item.data(6, Qt.ItemDataRole.UserRole))
                    f.write(f'{file_path}\n')

    def load_playlist(self):
        try:
            self.playlist.clear()  # 清空现有播放列表
            if Path('playlist.json').exists():
                with open('playlist.json', 'r', encoding='utf-8') as f:
                    playlist_data = json.load(f)
                
                for item_data in playlist_data:
                    file_path = Path(item_data['file_path'])
                    if file_path.exists():
                        self._add_to_playlist(str(file_path))
                    else:
                        logging.warning(f'文件不存在: {file_path}')
            # 初始化当前选中项
            if self.playlist.topLevelItemCount() > 0:
                self.playlist.setCurrentItem(self.playlist.topLevelItem(0))
        except Exception as e:
            logging.error(f'加载播放列表失败: {str(e)}')
            QMessageBox.critical(self, "加载错误", f"播放列表加载失败:\n{str(e)}")



if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = MainWindow()
    window.show()
    sys.exit(app.exec())